#include <sdk.h>
#include <wx/tokenzr.h>
#include <M_Code_StyledTextCtrl.h>
#include "parser.h"
#include "parserthreadedtask.h"
#include "../classbrowser.h"
#include "../classbrowserbuilderthread.h"

#define CC_PARSER_DEBUG_OUTPUT 0

namespace ParserCommon {
  static const int PARSER_BATCHPARSE_TIMER_DELAY = 300;
  static const int PARSER_BATCHPARSE_TIMER_RUN_IMMEDIATELY = 10;
  static const int PARSER_BATCHPARSE_TIMER_DELAY_LONG = 1000;
  static const int PARSER_REPARSE_TIMER_DELAY = 100;
  static volatile Parser* s_CurrentParser = nullptr;
  static wxMutex s_ParserMutex;
  int idParserStart = wxNewId();
  int idParserEnd = wxNewId();
}

Parser::Parser( wxEvtHandler* parent, M_Code_Project* project ) :
  m_Parent( parent ),
  m_Project( project ),
  m_UsingCache( false ),
  m_Pool( this, wxNewId(), 1, 2 * 1024 * 1024 ),
  m_IsParsing( false ),
  m_NeedsReparse( false ),
  m_IsFirstBatch( false ),
  m_ReparseTimer( this, wxNewId() ),
  m_BatchTimer( this, wxNewId() ),
  m_StopWatchRunning( false ),
  m_IgnoreThreadEvents( true ),
  m_IsBatchParseDone( false ),
  m_ParserState( ParserCommon::ptCreateParser ),
  m_NeedMarkFileAsLocal( true ) {
  ReadOptions();
  ConnectEvents();
}

Parser::~Parser() {
  DisconnectEvents();
  TerminateAllThreads();
  CC_LOCKER_TRACK_P_MTX_LOCK( ParserCommon::s_ParserMutex )
  if( ParserCommon::s_CurrentParser == this ) {
    ParserCommon::s_CurrentParser = nullptr;
  }
  CC_LOCKER_TRACK_P_MTX_UNLOCK( ParserCommon::s_ParserMutex )
}

void Parser::ConnectEvents() {
  Connect( m_Pool.GetId(), cbEVT_THREADTASK_ALLDONE,
           ( wxObjectEventFunction )( wxEventFunction )( wxCommandEventFunction )&Parser::OnAllThreadsDone );
  Connect( m_ReparseTimer.GetId(), wxEVT_TIMER, wxTimerEventHandler( Parser::OnReparseTimer ) );
  Connect( m_BatchTimer.GetId(), wxEVT_TIMER, wxTimerEventHandler( Parser::OnBatchTimer ) );
}

void Parser::DisconnectEvents() {
  Disconnect( m_Pool.GetId(), cbEVT_THREADTASK_ALLDONE,
              ( wxObjectEventFunction )( wxEventFunction )( wxCommandEventFunction )&Parser::OnAllThreadsDone );
  Disconnect( m_ReparseTimer.GetId(), wxEVT_TIMER, wxTimerEventHandler( Parser::OnReparseTimer ) );
  Disconnect( m_BatchTimer.GetId(), wxEVT_TIMER, wxTimerEventHandler( Parser::OnBatchTimer ) );
}

bool Parser::Done() {
  CC_LOCKER_TRACK_P_MTX_LOCK( ParserCommon::s_ParserMutex )
  bool done = m_BatchParseFiles.empty() && m_PredefinedMacros.IsEmpty() && !m_NeedMarkFileAsLocal && m_Pool.Done();
  CC_LOCKER_TRACK_P_MTX_UNLOCK( ParserCommon::s_ParserMutex )
  return done;
}

wxString Parser::NotDoneReason() {
  CC_LOCKER_TRACK_P_MTX_LOCK( ParserCommon::s_ParserMutex )
  wxString reason = _T( " > Reasons:" );
  if( !m_BatchParseFiles.empty() ) {
    reason += _T( "\n- still batch parse files to parse" );
  }
  if( !m_PredefinedMacros.IsEmpty() ) {
    reason += _T( "\n- still pre-defined macros to operate" );
  }
  if( m_NeedMarkFileAsLocal ) {
    reason += _T( "\n- still need to mark files as local" );
  }
  if( !m_Pool.Done() ) {
    reason += _T( "\n- thread pool is not done yet" );
  }
  CC_LOCKER_TRACK_P_MTX_UNLOCK( ParserCommon::s_ParserMutex )
  return reason;
}

void Parser::AddPredefinedMacros( const wxString& defs ) {
  if( m_BatchTimer.IsRunning() ) {
    m_BatchTimer.Stop();
  }
  CC_LOCKER_TRACK_P_MTX_LOCK( ParserCommon::s_ParserMutex )
  m_PredefinedMacros << defs;
  if( m_ParserState == ParserCommon::ptUndefined ) {
    m_ParserState = ParserCommon::ptCreateParser;
  }
  if( !m_IsParsing ) {
    m_BatchTimer.Start( ParserCommon::PARSER_BATCHPARSE_TIMER_DELAY, wxTIMER_ONE_SHOT );
  }
  CC_LOCKER_TRACK_P_MTX_UNLOCK( ParserCommon::s_ParserMutex )
}

void Parser::ClearPredefinedMacros() {
  CC_LOCKER_TRACK_P_MTX_LOCK( ParserCommon::s_ParserMutex )
  m_LastPredefinedMacros = m_PredefinedMacros;
  m_PredefinedMacros.Clear();
  CC_LOCKER_TRACK_P_MTX_UNLOCK( ParserCommon::s_ParserMutex );
}

const wxString Parser::GetPredefinedMacros() const {
  return m_LastPredefinedMacros;
}

void Parser::AddBatchParse( const StringList& filenames ) {
  if( m_BatchTimer.IsRunning() ) {
    m_BatchTimer.Stop();
  }
  CC_LOCKER_TRACK_P_MTX_LOCK( ParserCommon::s_ParserMutex )
  if( m_BatchParseFiles.empty() ) {
    m_BatchParseFiles = filenames;
  } else {
    std::copy( filenames.begin(), filenames.end(), std::back_inserter( m_BatchParseFiles ) );
  }
  if( m_ParserState == ParserCommon::ptUndefined ) {
    m_ParserState = ParserCommon::ptCreateParser;
  }
  if( !m_IsParsing ) {
    m_BatchTimer.Start( ParserCommon::PARSER_BATCHPARSE_TIMER_DELAY, wxTIMER_ONE_SHOT );
  }
  CC_LOCKER_TRACK_P_MTX_UNLOCK( ParserCommon::s_ParserMutex )
}

void Parser::AddParse( const wxString& filename ) {
  if( m_BatchTimer.IsRunning() ) {
    m_BatchTimer.Stop();
  }
  CC_LOCKER_TRACK_P_MTX_LOCK( ParserCommon::s_ParserMutex )
  m_BatchParseFiles.push_back( filename );
  if( !m_IsParsing ) {
    m_BatchTimer.Start( ParserCommon::PARSER_BATCHPARSE_TIMER_DELAY, wxTIMER_ONE_SHOT );
  }
  CC_LOCKER_TRACK_P_MTX_UNLOCK( ParserCommon::s_ParserMutex )
}

bool Parser::Parse( const wxString& filename, bool isLocal, bool locked ) {
  ParserThreadOptions opts;
  opts.useBuffer = false;
  opts.bufferSkipBlocks = false;
  opts.bufferSkipOuterBlocks = false;
  opts.followLocalIncludes = m_Options.followLocalIncludes;
  opts.followGlobalIncludes = m_Options.followGlobalIncludes;
  opts.wantPreprocessor = m_Options.wantPreprocessor;
  opts.parseComplexMacros = m_Options.parseComplexMacros;
  opts.platformCheck = m_Options.platformCheck;
  opts.storeDocumentation = m_Options.storeDocumentation;
  opts.loader = nullptr;
  bool result = false;
  do {
    bool canparse = false;
    {
      if( !locked )
        CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
        canparse = !m_TokenTree->IsFileParsed( filename );
      if( canparse ) {
        canparse = m_TokenTree->ReserveFileForParsing( filename, true ) != 0;
      }
      if( !locked )
        CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
      }
    if( !canparse ) {
      break;
    }
    opts.loader = Manager::Get()->GetFileManager()->Load( filename, m_NeedsReparse );
    ParserThread* thread = new ParserThread( this, filename, isLocal, opts, m_TokenTree );
    if( locked ) {
      CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
      wxMilliSleep( 1 );
      CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
      AddParserThread( thread );
      result = thread->Parse();
      RemoveParserThread( thread );
      delete thread;
      return true;
    } else {
      m_Pool.AddTask( thread, true );
    }
    result = true;
  } while( false );
  return result;
}

bool Parser::ParseBuffer( const wxString& buffer, bool isLocal, bool  bufferSkipBlocks, bool isTemp,
                          const wxString& filename, int  parentIdx, int initLine ) {
  ParserThreadOptions opts;
  opts.useBuffer = true;
  opts.fileOfBuffer = filename;
  opts.parentIdxOfBuffer = parentIdx;
  opts.initLineOfBuffer = initLine;
  opts.bufferSkipBlocks = bufferSkipBlocks;
  opts.isTemp = isTemp;
  opts.followLocalIncludes = false;
  opts.followGlobalIncludes = false;
  opts.wantPreprocessor = m_Options.wantPreprocessor;
  opts.parseComplexMacros = m_Options.parseComplexMacros;
  opts.platformCheck = true;
  opts.handleFunctions = true;
  opts.storeDocumentation = m_Options.storeDocumentation;
  ParserThread thread( this, buffer, isLocal, opts, m_TokenTree );
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  bool success = thread.Parse();
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  return success;
}

bool Parser::ParseBufferForFunctions( const wxString& buffer ) {
  ParserThreadOptions opts;
  opts.useBuffer = true;
  opts.bufferSkipBlocks = true;
  opts.followLocalIncludes = false;
  opts.followGlobalIncludes = false;
  opts.wantPreprocessor = m_Options.wantPreprocessor;
  opts.parseComplexMacros = m_Options.parseComplexMacros;
  opts.platformCheck = m_Options.platformCheck;
  opts.handleFunctions = true;
  opts.storeDocumentation = m_Options.storeDocumentation;
  ParserThread thread( this, buffer, false, opts, m_TempTokenTree );
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  bool success = thread.Parse();
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  return success;
}

bool Parser::ParseBufferForNamespaces( const wxString& buffer, NameSpaceVec& result ) {
  ParserThreadOptions opts;
  opts.useBuffer = true;
  opts.followLocalIncludes = false;
  opts.followGlobalIncludes = false;
  opts.wantPreprocessor = m_Options.wantPreprocessor;
  opts.parseComplexMacros = false;
  opts.platformCheck = true;
  opts.storeDocumentation = m_Options.storeDocumentation;
  ParserThread thread( this, wxEmptyString, true, opts, m_TempTokenTree );
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  bool success = thread.ParseBufferForNamespaces( buffer, result );
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  return success;
}

bool Parser::ParseBufferForUsingNamespace( const wxString& buffer, wxArrayString& result, bool bufferSkipBlocks ) {
  ParserThreadOptions opts;
  opts.useBuffer = true;
  opts.followLocalIncludes = false;
  opts.followGlobalIncludes = false;
  opts.wantPreprocessor = m_Options.wantPreprocessor;
  opts.parseComplexMacros = false;
  opts.platformCheck = true;
  opts.bufferSkipBlocks = bufferSkipBlocks;
  opts.storeDocumentation = m_Options.storeDocumentation;
  ParserThread thread( this, wxEmptyString, false, opts, m_TempTokenTree );
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  bool success = thread.ParseBufferForUsingNamespace( buffer, result );
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  return success;
}

bool Parser::RemoveFile( const wxString& filename ) {
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  const size_t fileIdx = m_TokenTree->InsertFileOrGetIndex( filename );
  const bool   result  = m_TokenTree->GetFileStatusCountForIndex( fileIdx );
  m_TokenTree->RemoveFile( filename );
  m_TokenTree->EraseFileMapInFileMap( fileIdx );
  m_TokenTree->EraseFileStatusByIndex( fileIdx );
  m_TokenTree->EraseFilesToBeReparsedByIndex( fileIdx );
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  return result;
}

bool Parser::AddFile( const wxString& filename, M_Code_Project* project, M_Code_unused bool isLocal ) {
  if( project != m_Project ) {
    return false;
  }
  if( IsFileParsed( filename ) ) {
    return false;
  }
  if( m_ParserState == ParserCommon::ptUndefined ) {
    m_ParserState = ParserCommon::ptAddFileToParser;
  }
  AddParse( filename );
  if( project ) {
    m_NeedMarkFileAsLocal = true;
  }
  return true;
}

bool Parser::Reparse( const wxString& filename, M_Code_unused bool isLocal ) {
  if( !Done() ) {
    wxString msg( _T( "Parser::Reparse : The Parser is not done." ) );
    msg += NotDoneReason();
    CCLogger::Get()->DebugLog( msg );
    return false;
  }
  if( m_ReparseTimer.IsRunning() ) {
    m_ReparseTimer.Stop();
  }
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  m_TokenTree->FlagFileForReparsing( filename );
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  m_NeedsReparse = true;
  m_ReparseTimer.Start( ParserCommon::PARSER_REPARSE_TIMER_DELAY, wxTIMER_ONE_SHOT );
  return true;
}

void Parser::TerminateAllThreads() {
  AbortParserThreads();
  m_Pool.AbortAllTasks();
  while( !m_Pool.Done() ) {
    wxMilliSleep( 1 );
  }
}

bool Parser::UpdateParsingProject( M_Code_Project* project ) {
  if( m_Project == project ) {
    return true;
  } else if( !Done() ) {
    wxString msg( _T( "Parser::UpdateParsingProject(): The Parser is not done." ) );
    msg += NotDoneReason();
    CCLogger::Get()->DebugLog( msg );
    return false;
  } else {
    m_Project = project;
    return true;
  }
}

void Parser::OnAllThreadsDone( CodeBlocksEvent& event ) {
  if( m_IgnoreThreadEvents || Manager::IsAppShuttingDown() ) {
    return;
  }
  if( event.GetId() != m_Pool.GetId() ) {
    CCLogger::Get()->DebugLog( _T( "Parser::OnAllThreadsDone(): Why is event.GetId() not equal m_Pool.GetId()?" ) );
    return;
  }
  if( !m_TokenTree ) {
    m_Throw( _T( "m_TokenTree is a nullptr?!" ) );
  }
  if( !m_IsParsing ) {
    CCLogger::Get()->DebugLog( _T( "Parser::OnAllThreadsDone(): Why is m_IsParsing false?" ) );
    return;
  }
  if( !m_PredefinedMacros.IsEmpty()
      || !m_BatchParseFiles.empty() ) {
    m_BatchTimer.Start( ParserCommon::PARSER_BATCHPARSE_TIMER_RUN_IMMEDIATELY, wxTIMER_ONE_SHOT );
  } else if( ( m_ParserState == ParserCommon::ptCreateParser || m_ParserState == ParserCommon::ptAddFileToParser )
             && m_NeedMarkFileAsLocal && m_Project ) {
    m_NeedMarkFileAsLocal = false;
    MarkFileAsLocalThreadedTask* thread = new MarkFileAsLocalThreadedTask( this, m_Project );
    m_Pool.AddTask( thread, true );
  } else {
    if( !m_Project ) {
      m_NeedMarkFileAsLocal = false;
    }
    m_IgnoreThreadEvents = true;
    m_NeedsReparse = false;
    m_IsParsing = false;
    m_IsBatchParseDone = true;
    EndStopWatch();
    wxString prj = ( m_Project ? m_Project->GetTitle() : _T( "*NONE*" ) );
    wxString parseEndLog;
    CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
    parseEndLog.Printf( _T( "项目 '%s' 分析完成 (%lu 文件总数, %lu 文件总尺寸 " ), prj.wx_str(),
                        m_TokenTree ? static_cast<unsigned long>( m_TokenTree->GetFileMapSize() ) : 0,
                        m_TokenTree ? static_cast<unsigned long>( m_TokenTree->realsize() ) : 0 );
    CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
    Manager::Get()->GetLogManager()->Log( parseEndLog );
    m_ParserState = ParserCommon::ptUndefined;
    ParserCommon::s_CurrentParser = nullptr;
  }
}

bool Parser::ParseFile( const wxString& filename, bool isGlobal, bool locked ) {
  if( ( !isGlobal && !m_Options.followLocalIncludes ) || ( isGlobal && !m_Options.followGlobalIncludes ) ) {
    return false;
  }
  if( filename.IsEmpty() ) {
    return false;
  }
  const bool ret = Parse( filename, !isGlobal, locked );
  return ret;
}

void Parser::StartStopWatch() {
  if( !m_StopWatchRunning ) {
    m_StopWatchRunning = true;
    m_StopWatch.Start();
  }
}

void Parser::EndStopWatch() {
  if( m_StopWatchRunning ) {
    m_StopWatch.Pause();
    m_StopWatchRunning = false;
  }
}

void Parser::OnReparseTimer( wxTimerEvent& event ) {
  ReparseModifiedFiles();
  event.Skip();
}

void Parser::OnBatchTimer( M_Code_unused wxTimerEvent& event ) {
  if( Manager::IsAppShuttingDown() ) {
    return;
  }
  if( ParserCommon::s_CurrentParser && ParserCommon::s_CurrentParser != this ) {
    m_BatchTimer.Start( ParserCommon::PARSER_BATCHPARSE_TIMER_DELAY_LONG, wxTIMER_ONE_SHOT );
    return;
  }
  StartStopWatch();
  if( m_BatchParseFiles.empty() && m_PredefinedMacros.IsEmpty() ) {
    return;
  }
  bool send_event = true;
  bool sendStartParseEvent = false;
  if( !m_BatchParseFiles.empty() || !m_PredefinedMacros.IsEmpty() ) {
    CC_LOCKER_TRACK_P_MTX_LOCK( ParserCommon::s_ParserMutex )
    ParserThreadedTask* thread = new ParserThreadedTask( this, ParserCommon::s_ParserMutex );
    m_Pool.AddTask( thread, true );
    if( ParserCommon::s_CurrentParser ) {
      send_event = false;
    } else {
      ParserCommon::s_CurrentParser = this;
      m_StopWatch.Start();
      sendStartParseEvent = true;
    }
    CC_LOCKER_TRACK_P_MTX_UNLOCK( ParserCommon::s_ParserMutex )
  }
  if( send_event ) {
    if( sendStartParseEvent ) {
      ProcessParserEvent( m_ParserState, ParserCommon::idParserStart );
    } else {
      ProcessParserEvent( ParserCommon::ptUndefined, ParserCommon::idParserStart, _T( "Unexpected behaviour!" ) );
    }
  }
}

void Parser::ReparseModifiedFiles() {
  if( !Done() ) {
    wxString msg( _T( "Parser::ReparseModifiedFiles : The Parser is not done." ) );
    msg += NotDoneReason();
    CCLogger::Get()->DebugLog( msg );
    m_ReparseTimer.Start( ParserCommon::PARSER_REPARSE_TIMER_DELAY, wxTIMER_ONE_SHOT );
    return;
  }
  if( !m_NeedsReparse ) {
    m_NeedsReparse = true;
  }
  std::queue<size_t> files_idx;
  std::queue<wxString> files_list;
  TokenFileSet::const_iterator it;
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  for( it = m_TokenTree->GetFilesToBeReparsed()->begin(); it != m_TokenTree->GetFilesToBeReparsed()->end(); ++it ) {
    wxString filename = m_TokenTree->GetFilename( *it );
    if( FileTypeOf( filename ) == ftSource || FileTypeOf( filename ) == ftTemplateSource ) {
      continue;
    }
    files_list.push( filename );
    files_idx.push( *it );
  }
  for( it = m_TokenTree->GetFilesToBeReparsed()->begin(); it != m_TokenTree->GetFilesToBeReparsed()->end(); ++it ) {
    wxString filename = m_TokenTree->GetFilename( *it );
    if( FileTypeOf( filename ) != ftSource && FileTypeOf( filename ) != ftTemplateSource ) {
      continue;
    }
    files_list.push( filename );
    files_idx.push( *it );
  }
  while( !files_idx.empty() ) {
    m_TokenTree->RemoveFile( files_idx.front() );
    files_idx.pop();
  }
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  if( !files_list.empty() && m_ParserState == ParserCommon::ptUndefined ) {
    m_ParserState = ParserCommon::ptReparseFile;
  } else {
    m_NeedsReparse = false;
  }
  while( !files_list.empty() ) {
    AddParse( files_list.front() );
    files_list.pop();
  }
}

bool Parser::IsFileParsed( const wxString& filename ) {
  bool isParsed = false;
  CC_LOCKER_TRACK_TT_MTX_LOCK( s_TokenTreeMutex )
  isParsed = m_TokenTree->IsFileParsed( filename );
  CC_LOCKER_TRACK_TT_MTX_UNLOCK( s_TokenTreeMutex )
  if( !isParsed ) {
    CC_LOCKER_TRACK_P_MTX_LOCK( ParserCommon::s_ParserMutex )
    StringList::iterator it = std::find( m_BatchParseFiles.begin(), m_BatchParseFiles.end(), filename );
    isParsed = it != m_BatchParseFiles.end();
    CC_LOCKER_TRACK_P_MTX_UNLOCK( ParserCommon::s_ParserMutex )
  }
  return isParsed;
}

void Parser::ProcessParserEvent( ParserCommon::ParserState state, int id, const wxString& info ) {
  wxCommandEvent evt( wxEVT_COMMAND_MENU_SELECTED, id );
  evt.SetEventObject( this );   // Parser*
  evt.SetClientData( m_Project ); // Project*
  evt.SetInt( state );
  evt.SetString( info );
  m_Parent->ProcessEvent( evt );
}

void Parser::ReadOptions() {
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "code_completion" ) );
  bool force_all_on = !cfg->ReadBool( _T( "/parser_defaults_changed" ), false );
  if( force_all_on ) {
    cfg->Write( _T( "/parser_defaults_changed" ), true );
    cfg->Write( _T( "/parser_follow_local_includes" ), true );
    cfg->Write( _T( "/parser_follow_global_includes" ), true );
    cfg->Write( _T( "/want_preprocessor" ), true );
    cfg->Write( _T( "/parse_complex_macros" ), true );
    cfg->Write( _T( "/platform_check" ), true );
  }
  m_Options.useSmartSense = cfg->ReadBool( _T( "/use_SmartSense" ), true );
  m_Options.whileTyping = cfg->ReadBool( _T( "/while_typing" ), true );
  ConfigManager* ccmcfg = Manager::Get()->GetConfigManager( _T( "ccmanager" ) );
  m_Options.caseSensitive = ccmcfg->ReadBool( _T( "/case_sensitive" ), false );
  m_Options.followLocalIncludes = cfg->ReadBool( _T( "/parser_follow_local_includes" ), true );
  m_Options.followGlobalIncludes = cfg->ReadBool( _T( "/parser_follow_global_includes" ), true );
  m_Options.wantPreprocessor = cfg->ReadBool( _T( "/want_preprocessor" ), true );
  m_Options.parseComplexMacros = cfg->ReadBool( _T( "/parse_complex_macros" ), true );
  m_Options.platformCheck = cfg->ReadBool( _T( "/platform_check" ), true );
  m_BrowserOptions.showInheritance = cfg->ReadBool( _T( "/browser_show_inheritance" ), false );
  m_BrowserOptions.expandNS = cfg->ReadBool( _T( "/browser_expand_ns" ), false );
  m_BrowserOptions.treeMembers = cfg->ReadBool( _T( "/browser_tree_members" ), true );
  m_BrowserOptions.displayFilter = ( BrowserDisplayFilter )cfg->ReadInt( _T( "/browser_display_filter" ), bdfFile );
  m_BrowserOptions.sortType = ( BrowserSortType )cfg->ReadInt( _T( "/browser_sort_type" ),           bstKind );
  m_Options.storeDocumentation = cfg->ReadBool( _T( "/use_documentation_helper" ),         false );
  ParserCommon::EFileType ft_dummy = ParserCommon::FileType( wxEmptyString, true );
  wxUnusedVar( ft_dummy );
}

void Parser::WriteOptions() {
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "code_completion" ) );
  cfg->Write( _T( "/use_SmartSense" ), m_Options.useSmartSense );
  cfg->Write( _T( "/while_typing" ), m_Options.whileTyping );
  cfg->Write( _T( "/parser_follow_local_includes" ),  m_Options.followLocalIncludes );
  cfg->Write( _T( "/parser_follow_global_includes" ), m_Options.followGlobalIncludes );
  cfg->Write( _T( "/want_preprocessor" ), m_Options.wantPreprocessor );
  cfg->Write( _T( "/parse_complex_macros" ), m_Options.parseComplexMacros );
  cfg->Write( _T( "/platform_check" ), m_Options.platformCheck );
  cfg->Write( _T( "/browser_show_inheritance" ), m_BrowserOptions.showInheritance );
  cfg->Write( _T( "/browser_expand_ns" ), m_BrowserOptions.expandNS );
  cfg->Write( _T( "/browser_tree_members" ), m_BrowserOptions.treeMembers );
  cfg->Write( _T( "/browser_display_filter" ), m_BrowserOptions.displayFilter );
  cfg->Write( _T( "/browser_sort_type" ), m_BrowserOptions.sortType );
}

void Parser::AddParserThread( cbThreadedTask* task ) {
  if( task ) {
    m_tasksQueue.push_back( task );
  }
}

void Parser::RemoveParserThread( cbThreadedTask* task ) {
  if( task &&  m_tasksQueue.size() ) {
    m_tasksQueue.pop_back();
  }
}

void Parser::AbortParserThreads() {
  if( m_tasksQueue.size() ) {
    for( TasksQueue::iterator it = m_tasksQueue.begin(); it != m_tasksQueue.end(); ++it ) {
      ( *it )->Abort();
    }
  }
}
